package cn.alpha.open.autocode.convert;

import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import com.google.common.collect.Lists;

import cn.alpha.open.autocode.mavenplugin.ConvertCfg;
import cn.alpha.open.autocode.mavenplugin.TemplateCfg;
import cn.alpha.open.autocode.model.ClassModel;
import cn.alpha.open.autocode.model.ColumnMeta;
import cn.alpha.open.autocode.model.ControllerModel;
import cn.alpha.open.autocode.model.DataTypeMap;
import cn.alpha.open.autocode.model.EntityFieldModel;
import cn.alpha.open.autocode.model.EntityModel;
import cn.alpha.open.autocode.model.MapperInterfaceModel;
import cn.alpha.open.autocode.model.MapperXmlModel;
import cn.alpha.open.autocode.model.ServiceInterfaceModel;
import cn.alpha.open.autocode.model.ServiceModel;
import cn.alpha.open.autocode.model.TableMeta;

public class ModelConvertor 
{
	private static Pattern linePattern = Pattern.compile("_(\\w)");
	
	private List<TableMeta> tableMetas;
	
	private ConvertCfg convertCfg;
	private TemplateCfg templateCfg;
	private String[] removeTablePrefixs;
	
	private List<ServiceModel> serivceModels=Lists.newArrayList();
	private List<ServiceInterfaceModel> serivceInterfaceModels=Lists.newArrayList();
	private List<EntityModel> entityModels=Lists.newArrayList();
	private List<MapperInterfaceModel> mappingInterfaceSModels=Lists.newArrayList();
	private List<MapperXmlModel> mappingXmlModels=Lists.newArrayList();
	private List<ControllerModel> controllerModels=Lists.newArrayList();
	
	public ModelConvertor(List<TableMeta> tableMetas,ConvertCfg convertCfg,TemplateCfg templateCfg)
	{
		this.tableMetas=tableMetas;
		this.convertCfg=convertCfg;
		this.templateCfg=templateCfg;
		String removeTablePrefixStr=this.convertCfg.getRemoveTablePrefixs();
		if(removeTablePrefixStr!=null)
			removeTablePrefixs=removeTablePrefixStr.split(",");
	}
	
	private void convertTable(TableMeta tableMeta)
	{
		EntityModel entityModel=convertEntity(tableMeta);
		entityModels.add(entityModel);
		
		MapperInterfaceModel mapperInterfaceModel=convertMapperInterface(tableMeta);
		mapperInterfaceModel.setEntityModel(entityModel);
		mappingInterfaceSModels.add(mapperInterfaceModel);
		
		MapperXmlModel mapperXmlModel=new MapperXmlModel();
		mapperXmlModel.setEntityModel(entityModel);
		mapperXmlModel.setMapperInterfaceModel(mapperInterfaceModel);
		mappingXmlModels.add(mapperXmlModel);
		
		if(tableMeta.isHasSinglePK())
		{
			ServiceModel serviceModel=convertServiceModel(tableMeta);
			serviceModel.setEntityModel(entityModel);
			serviceModel.setMapperInterfaceModel(mapperInterfaceModel);
			serivceModels.add(serviceModel);
			
			ServiceInterfaceModel serviceInterfaceModel=convertServiceInterfaceModel(tableMeta);
			serviceInterfaceModel.setEntityModel(entityModel);
			serivceInterfaceModels.add(serviceInterfaceModel);
			
			ControllerModel controllerModel=convertControllerModel(tableMeta);
			controllerModel.setServiceInterfaceModel(serviceInterfaceModel);
			controllerModels.add(controllerModel);
		}
	}
	
	
	private void convertClass(ClassModel res,TableMeta tableMeta)
	{
		String tableName=tableMeta.getTableName();
		res.setTableName(tableName);
		res.setInstanceName(StringUtils.uncapitalize(res.getClassName()));
	}
	
	private ControllerModel convertControllerModel(TableMeta tableMeta)
	{
		ControllerModel res=new ControllerModel();
		String tableName=tableMeta.getTableName();
		res.setPackageNamePrefix(getPackagePrefix(tableName,templateCfg.getControllerPackageSignal()));
		res.setClassName(getClassName(tableName)+"Controller");
		res.setSuperClass(templateCfg.getControllerSuperClass());
		convertClass(res,tableMeta);
		return res;
	}
	
	private ServiceInterfaceModel convertServiceInterfaceModel(TableMeta tableMeta)
	{
		ServiceInterfaceModel res=new ServiceInterfaceModel();
		String tableName=tableMeta.getTableName();
		res.setPackageNamePrefix(getPackagePrefix(tableName,templateCfg.getServicePackageSignal()));
		res.setClassName(getClassName(tableName)+"Service");
		res.setSuperClass(templateCfg.getServiceInterfaceSuperClass());
		convertClass(res,tableMeta);
		return res;
	}
	
	private ServiceModel convertServiceModel(TableMeta tableMeta)
	{
		ServiceModel res=new ServiceModel();
		String tableName=tableMeta.getTableName();
		res.setPackageNamePrefix(getPackagePrefix(tableName,templateCfg.getServicePackageSignal())+".impl");
		res.setClassName(getClassName(tableName)+"ServiceImpl");
		res.setSuperClass(templateCfg.getServiceImplSuperClass());
		convertClass(res,tableMeta);
		return res;
	}
	
	private MapperInterfaceModel convertMapperInterface(TableMeta tableMeta)
	{
		MapperInterfaceModel res=new MapperInterfaceModel();
		String tableName=tableMeta.getTableName();
		res.setPackageNamePrefix(getPackagePrefix(tableName,templateCfg.getMapperPackageSignal()));
		res.setClassName(getClassName(tableName)+"Mapper");
		res.setSuperClass(templateCfg.getMapperSuperClass());
		convertClass(res,tableMeta);
		return res;
	}
	
	private EntityModel convertEntity(TableMeta tableMeta)
	{
		EntityModel res=new EntityModel();
		String tableName=tableMeta.getTableName();
		res.setPackageNamePrefix(getPackagePrefix(tableName,templateCfg.getEntityPackageSignal()));
		res.setClassName(getClassName(tableName));
		res.setTableComment(tableMeta.getTableComment());
		res.setHasPK(tableMeta.isHasSinglePK());
		res.setSuperClass(templateCfg.getEntitySuperClass());
		convertClass(res,tableMeta);
		List<EntityFieldModel> fields=Lists.newArrayList();
		for(ColumnMeta columnMeta:tableMeta.getColumnMetas())
			fields.add(convertEntityField(columnMeta));
		res.setFields(fields);
		
		if(res.isHasPK())
			res.setPkField(convertEntityField(tableMeta.getPkColumn()));
		
		return res;
	}
	
	private EntityFieldModel convertEntityField(ColumnMeta columnMeta)
	{
		EntityFieldModel res=new EntityFieldModel();
		String columnName=columnMeta.getColumnName();
		res.setColumnName(columnName);
		String methodName=StringUtils.capitalize(camel(columnName));
		res.setMethodName(methodName);
		res.setComment(columnMeta.getColumnComment() );
		res.setFieldName(StringUtils.uncapitalize(methodName));
		res.setFieldType(DataTypeMap.getDataTypeName(columnMeta.getDataType()));
		return res;
	}
	
	private String getPackagePrefix(String tableName,String signal)
	{
		String packagePrefix=templateCfg.getPacakgeNamePrefix();
		Map<String,String> prefixPackage=convertCfg.getTableModuleMap();
		if(CollectionUtils.isEmpty(prefixPackage))
			return packagePrefix;
		
		String module="";
		for(Map.Entry<String, String> entry:prefixPackage.entrySet())
		{
			if(tableName.startsWith(entry.getKey()))
			{
				module=entry.getValue();
				break;
			}
		}
		if(StringUtils.isEmpty(signal))
		{
			packagePrefix=packagePrefix+"."+module;
		}
		else
		{
			if(templateCfg.isSignalPre())
				packagePrefix=packagePrefix+"."+signal+"."+module;
			else
				packagePrefix=packagePrefix+"."+module+"."+signal;
				
		}
		return packagePrefix;
	}
	
	private String getClassName(String tableName)
	{
		if(removeTablePrefixs==null)
			return StringUtils.capitalize(camel(tableName));
		
		for(String prefix:removeTablePrefixs)
		{
			if(tableName.startsWith(prefix))
			{
				tableName=tableName.substring(prefix.length());
				break;
			}
		}
		return StringUtils.capitalize(camel(tableName));
	}
	
	private String camel(String name)
	{
		name = name.toLowerCase();  
        Matcher matcher = linePattern.matcher(name);  
        StringBuffer sb = new StringBuffer();  
        while(matcher.find())
            matcher.appendReplacement(sb, matcher.group(1).toUpperCase());  
        matcher.appendTail(sb);  
        return sb.toString();  
	}

	
	public void convert()
	{
		for(TableMeta tableMeta:tableMetas)
			convertTable(tableMeta);
	}
	
	public List<TableMeta> getTableMetas() {
		return tableMetas;
	}

	public List<ServiceModel> getSerivceModels() {
		return serivceModels;
	}

	public List<EntityModel> getEntityModels() {
		return entityModels;
	}

	public List<MapperInterfaceModel> getMappingInterfaceModels() {
		return mappingInterfaceSModels;
	}

	public List<MapperXmlModel> getMappingXmlModels() {
		return mappingXmlModels;
	}

	public List<ControllerModel> getControllerModels() {
		return controllerModels;
	}

	public List<ServiceInterfaceModel> getSerivceInterfaceModels() {
		return serivceInterfaceModels;
	}
}
